package com.zkh.myutils.proxy;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.security.PrivilegedExceptionAction;
import java.security.ProtectionDomain;
import java.util.ArrayList;
import java.util.List;

import com.zkh.myutils.utils.UtilsException;

/**
 * 加载字节数组类，使用CGLIB3的实现方法
 * @author zkh
 */
public class ClassLoaderUtils {

	//定义类返回方法
	private static Method DEFINE_CLASS, DEFINE_CLASS_UNSAFE;
	private static final ProtectionDomain PROTECTION_DOMAIN;
	private static final Object UNSAFE;
	private static final Throwable THROWABLE;
	private static final List<Method> OBJECT_METHODS = new ArrayList<Method>();

	static {
		//定义全局变量
		ProtectionDomain protectionDomain;
		Method defineClass, defineClassUnsafe;
		Object unsafe;
		Throwable throwable = null;
		//获取相关数据
		try {
			protectionDomain = getProtectionDomain(ClassLoaderUtils.class);
			try {
				defineClass = (Method) AccessController.doPrivileged(new PrivilegedExceptionAction<Object>() {
					public Object run() throws Exception {
						//反射ClassLoader
						Class<?> loader = Class.forName("java.lang.ClassLoader");
						//获取defineClass方法
						Method defineClass = loader.getDeclaredMethod("defineClass",
								new Class[]{ String.class, byte[].class, Integer.TYPE, Integer.TYPE, ProtectionDomain.class});
						//允许访问
						defineClass.setAccessible(true);
						//返回该方法
						return defineClass;
					}
				});
				defineClassUnsafe = null;
				unsafe = null;
			} catch (Throwable t) {
				//如果抛出了异常
				throwable = t;
				defineClass = null;
				unsafe = AccessController.doPrivileged(new PrivilegedExceptionAction<Object>() {
					public Object run() throws Exception {
						Class<?> u = Class.forName("sun.misc.Unsafe");
						Field theUnsafe = u.getDeclaredField("theUnsafe");
						theUnsafe.setAccessible(true);
						return theUnsafe.get(null);
					}
				});
				Class<?> u = Class.forName("sun.misc.Unsafe");
				defineClassUnsafe = u.getMethod("defineClass",
						new Class[]{ String.class, byte[].class, Integer.TYPE, Integer.TYPE, ClassLoader.class, ProtectionDomain.class});
			}
			//获取对象的所有方法
			AccessController.doPrivileged(new PrivilegedExceptionAction<Object>() {
				public Object run() throws Exception {
					Method[] methods = Object.class.getDeclaredMethods();
					for (Method method : methods) {
						if ("finalize".equals(method.getName()) || (method.getModifiers() & (Modifier.FINAL | Modifier.STATIC)) > 0) {
							continue;
						}
						OBJECT_METHODS.add(method);
					}
					return null;
				}
			});
		} catch (Throwable t) {
			if (throwable == null) {
				throwable = t;
			}
			protectionDomain = null;
			defineClass = null;
			defineClassUnsafe = null;
			unsafe = null;
		}
		PROTECTION_DOMAIN = protectionDomain;
		DEFINE_CLASS = defineClass;
		DEFINE_CLASS_UNSAFE = defineClassUnsafe;
		UNSAFE = unsafe;
		THROWABLE = throwable;
	}
    
	/**
	 * 从指定类中获取保护域对象
	 * @param source 目标Classs
	 */
	public static ProtectionDomain getProtectionDomain(final Class<?> source) {
		//为空
		if(source == null) {
			return null;
		}
		//不为空，获取保护域对象
		return (ProtectionDomain) AccessController.doPrivileged(new PrivilegedAction<Object>() {
			public Object run() {
				return source.getProtectionDomain();
			}
		});
	}
	
	/**
	 * 加载一个字节数组Class
	 * @param className 类名描述符
	 * @param classBytes 类的字节数组
	 * @param loader 加载器
	 */
	public static Class<?> defineClass(String className, byte[] classBytes, ClassLoader loader) throws Exception {
		return defineClass(className, classBytes, loader, PROTECTION_DOMAIN);
	}

	/**
	 * 加载一个字节数组Class
	 * @param className 类名描述符
	 * @param classBytes 类的字节数组
	 * @param loader 加载器
	 * @param protectionDomain 指定保护域
	 */
	public static Class<?> defineClass(String className, byte[] classBytes, ClassLoader loader, ProtectionDomain protectionDomain) throws Exception {
	    Class<?> c;
	    if (DEFINE_CLASS != null) {
	        Object[] args = new Object[]{className, classBytes, new Integer(0), new Integer(classBytes.length), protectionDomain};
	        c = (Class<?>)DEFINE_CLASS.invoke(loader, args);
	    } else if (DEFINE_CLASS_UNSAFE != null) {
	        Object[] args = new Object[]{className, classBytes, new Integer(0), new Integer(classBytes.length), loader, protectionDomain};
	        c = (Class<?>)DEFINE_CLASS_UNSAFE.invoke(UNSAFE, args);
	    } else {
	        throw new UtilsException(THROWABLE);
	    }
	    // Force static initializers to run.
	    Class.forName(className, true, loader);
	    //返回Class
	    return c;
	}
}
